package main.java.LeetCode.ClassicalAlgorithm.Queue.Impl;





import main.java.LeetCode.ClassicalAlgorithm.Queue.Queue;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BlockQueue<E> implements Queue<E> {
    private final List<E> list;
    private final int cap;
    private final Lock lock;

    private final Condition isEmpty;
    private final Condition isFull;

    public BlockQueue(List<E> list){
        this(8,list,new ReentrantLock());
    }
    public BlockQueue(int cap,List<E> list,Lock lock){
        this.list = list;
        this.cap = cap;
        this.lock = lock;
        this.isFull = lock.newCondition();
        this.isEmpty = lock.newCondition();
    }
    public BlockQueue(){
        this(new ArrayList<E>());
    }

    public BlockQueue(int cap){
        this(cap,new ArrayList<>(),new ReentrantLock());
    }

    @Override
    public E get() {
        E e = null;
        lock.lock();
        try {
            while (list.isEmpty()) {
                isEmpty.await();
            }
            e = list.remove(0);
            isFull.signal();
        }catch (InterruptedException ignored){
        } finally{
            lock.unlock();
        }
        return e;
    }

    @Override
    public boolean set(E e) {
        lock.lock();
        try {
            while (list.size() == cap) {
                isFull.await();
            }
            list.add(e);
            isEmpty.signal();
            return true;
        }catch (InterruptedException exception){
            exception.printStackTrace(System.out);
        }finally {
            lock.unlock();
        }
        return false;
    }

    @Override
    public E get(long timeout, TimeUnit unit) {
        long nanos = unit.toNanos(timeout);
        E e = null;
        lock.lock();
        try {
            while (list.isEmpty()) {
                if(nanos<0)
                    return e;
                nanos = isEmpty.awaitNanos(nanos);
            }
            e = list.remove(0);
            isFull.signal();
        }catch (InterruptedException ignored){
        } finally{
            lock.unlock();
        }
        return e;
    }

    @Override
    public boolean offer(E e){
        try {
            lock.lock();
            if (list.size() == cap)
                return false;
            list.add(e);
            isEmpty.signal();
            return true;
        }finally {
            lock.unlock();
        }
    }

    @Override
    public int size() {
        return this.cap;
    }
}
